package com.licq.ldoc.domain;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;

/**
 * 
 * @author liujian
 *
 */
public abstract class ElementType {
	
	public abstract String getLabel();
	
	public abstract Class<?> getClazz();
	
	public static ElementType type(Class<?> clazz) {
		return new DefaultElementType(clazz);
	}
	
	public static ElementType map(Class<?> kClazz, Class<?> vClazz) {
		return new MapElementType(type(kClazz), type(vClazz));
	}
	
	public static ElementType collection(Class<?> clazz) {
		return new CollectionElementType(type(clazz));
	}
	
	public static ElementType array(Class<?> clazz) {
		return new ArrayElementType(type(clazz));
	}
	
	public static boolean isWrapperType(ElementType type) {
		return CollectionElementType.class.isInstance(type)
			|| ArrayElementType.class.isInstance(type);
	}
	
	public static boolean isMapperType(ElementType type) {
		return MapElementType.class.isInstance(type);
	}
	
	public static final class DefaultElementType extends ElementType {
		private Class<?> clazz;
		
		private DefaultElementType(Class<?> clazz) {
			this.clazz = clazz;
		}

		@Override
		public Class<?> getClazz() {
			return clazz;
		}
		
		@Override
		public String getLabel() {
			return clazz.getSimpleName();
		}
	}
	
	public abstract static class ElementTypeDecorator extends ElementType {
		protected ElementType elementType;

		protected ElementTypeDecorator(ElementType elementType) {
			this.elementType = elementType;
		}
		
		public abstract String getLabel();

		public ElementType getElementType() {
			return elementType;
		}
		
	}
	
	public static class CollectionElementType extends ElementTypeDecorator {

		public CollectionElementType(ElementType elementType) {
			super(elementType);
		}
		
		@Override
		public String getLabel() {
			return "<" + elementType.getLabel() + ">";
		}
		
		@Override
		public Class<?> getClazz() {
			return Collection.class;
		}
		
	}
	
	public static class ArrayElementType extends ElementTypeDecorator {
		
		public ArrayElementType(ElementType elementType) {
			super(elementType);
		}
		
		@Override
		public String getLabel() {
			return "[" + elementType.getLabel() + "]";
		}
		
		@Override
		public Class<?> getClazz() {
			return Array.class;
		}
		
	}
	
	public static class MapElementType extends ElementType {
		
		private ElementType keyElementType;
		
		private ElementType valueElementType;
		
		public MapElementType(ElementType keyElementType, 
				ElementType valueElementType) {
			this.keyElementType = keyElementType;
			this.valueElementType = valueElementType;
		}
		
		@Override
		public String getLabel() {
			return String.format("<%s,%s>", keyElementType.getLabel(),
					valueElementType.getLabel());
		}
		
		@Override
		public Class<?> getClazz() {
			return Map.class;
		}

		public ElementType getKeyElementType() {
			return keyElementType;
		}

		public ElementType getValueElementType() {
			return valueElementType;
		}
		
	}
}
